/*
 * Copyright 2020 Linkel Technology Co., Ltd, Beijing
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BA SIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/*
* Author: jaytsang <beaclnd92@gmail.com>
* Created at: 2020-09-10T14:09:00+08
*/

import config from "config";
import RepChainSynchronizer from "repchain-synchronizer";
import { prisma } from "../database/generated/prisma-client";
import Courier from "./Courier";
import BlockObserver from "./BlockObserver";
import logger from "./log";
import path from "path";
import enableStatusReportServer from "./status";

const repchainSynchronizers = {};

const myConfig = config.util.loadFileConfigs(path.join(__dirname, "..", "config"));

const { networks = [] } = myConfig;

const start = async () => {
    for (let network of networks) {
        const { id: blockchainId, peers = [] } = network;
        if (peers.length === 0) return;

        await prisma.upsertNetwork({
            where: { id: blockchainId },
            create: { id: blockchainId, NAME: blockchainId },
            update: { NAME: blockchainId },
        });

        const defaultAgentPeer = peers[0];
        const repChainSynchronizer = new RepChainSynchronizer({
            blockchainId,
            url: defaultAgentPeer.url_api,
            subscribeUrl: defaultAgentPeer.url_subscribe,
            prisma,
            logPath: myConfig.Log.repchainSynchronizerLogPath,
            onNetworkError: (changeRepchainClientApiUrl, changeRepchainEventTubeWsUrl) => {
                const rs = repchainSynchronizers[blockchainId];
                if (!rs) return;
                rs.currentConnectingPeerIndex += 1;
                if (rs.currentConnectingPeerIndex >= peers.length) {
                    rs.currentConnectingPeerIndex = 0;
                }
                const agentPeer = peers[rs.currentConnectingPeerIndex];

                changeRepchainClientApiUrl(agentPeer.url_api);
                changeRepchainEventTubeWsUrl(agentPeer.url_subscribe);
            }
        });
        repchainSynchronizers[blockchainId] = { currentConnectingPeerIndex: 0 };


        // 为出块事件订阅观察者，出现新区块则向已订阅区块数据的目标系统进行推送
        const subscribers = myConfig.subscribers;
        const blockObservers = [];
        for (let subscriber of subscribers) {
            if (subscriber.networkIds.includes(blockchainId)) {
                // courier负责实际推送区块数据，并定时检查处理未推送成功的区块数据
                const courier = new Courier(subscriber);
                await courier.init(blockchainId);
                blockObservers.push(new BlockObserver(courier, subscriber));
            }
        }
        blockObservers.forEach((blockObserver) => repChainSynchronizer.subscribeObserver(blockObserver, "block"));

        const { enableSync = true } = myConfig;
        if (enableSync) {
            // 开始同步区块链数据
            repChainSynchronizer.startSync();
            logger.info(`Started synchronize blockchain data from the blockchain peer: ${defaultAgentPeer.url_api} for blockchain network: ${blockchainId}`);
        }
    }
};

enableStatusReportServer();

start();
